package com.psedu.exam.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.psedu.exam.domain.AnswerOption;
import com.psedu.exam.domain.vo.AnswerQuestion;
import com.psedu.exam.domain.vo.QuestionDetail;
import com.psedu.exam.domain.vo.QuestionVo;
import com.psedu.exam.mapper.AnswerOptionMapper;
import com.psedu.common.core.utils.DateUtils;
import com.psedu.common.core.utils.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.psedu.exam.mapper.QuestionMapper;
import com.psedu.exam.domain.Question;
import com.psedu.exam.service.IQuestionService;
import org.springframework.transaction.annotation.Transactional;

/**
 * 候选题目Service业务层处理
 * 
 * @author mingyue
 * @date 2022-03-30
 */
@Service
public class QuestionServiceImpl implements IQuestionService 
{
    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private AnswerOptionMapper answerOptionMapper;

    /**
     * 查询候选题目
     * 
     * @param questionId 候选题目主键
     * @return 候选题目
     */
    @Override
    public Question selectQuestionByQuestionId(Long questionId)
    {
        return questionMapper.selectQuestionByQuestionId(questionId);
    }

    /**
     * 查询候选题目列表
     * 
     * @param question 候选题目
     * @return 候选题目
     */
    @Override
    public List<Question> selectQuestionList(Question question)
    {
        return questionMapper.selectQuestionList(question);
    }

    /**
     * 新增候选题目
     * 
     * @param question 候选题目
     * @return 结果
     */
    @Override
    public int insertQuestion(Question question)
    {
        question.setCreateTime(DateUtils.getNowDate());
        return questionMapper.insertQuestion(question);
    }

    /**
     * 修改候选题目
     * 
     * @param question 候选题目
     * @return 结果
     */
    @Override
    public int updateQuestion(Question question)
    {
        question.setUpdateTime(DateUtils.getNowDate());
        return questionMapper.updateQuestion(question);
    }

    /**
     * 批量删除候选题目
     * 
     * @param questionIds 需要删除的候选题目主键
     * @return 结果
     */
    @Override
    public int deleteQuestionByQuestionIds(Long[] questionIds)
    {
        return questionMapper.deleteQuestionByQuestionIds(questionIds);
    }

    /**
     * 删除候选题目信息
     * 
     * @param questionId 候选题目主键
     * @return 结果
     */
    @Override
    public int deleteQuestionByQuestionId(Long questionId)
    {
        return questionMapper.deleteQuestionByQuestionId(questionId);
    }

    /**
     * 添加试题和选项、答案
     * @param questionVo questionVo
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertQuestionAndAnswer(QuestionVo questionVo) {
        Question question = new Question();
        BeanUtils.copyProperties(questionVo, question);
        questionMapper.insert(question);
        List<AnswerOption> answerOptions = questionVo.getAnswerOptions();
        // 关联试题
        answerOptions.forEach(answerOption -> {
            answerOption.setQuestionId(question.getQuestionId());
        });
        // TODO 全空报错
        // 清除空选项
        answerOptions = rmEmptyOptions(answerOptions);
        boolean answerRes = answerOptionMapper.insertBatch(answerOptions);
        return 1;
    }

    /**
     * 清除空选项
     * @param answerOptions 待修改的选项列表
     * @return 新的选项列表
     */
    private List<AnswerOption> rmEmptyOptions(List<AnswerOption> answerOptions) {
        answerOptions = answerOptions.stream().filter(answerOption ->
                StringUtils.isNotEmpty(answerOption.getContent())
                        || StringUtils.isNotEmpty(answerOption.getImageUrl())
        ).collect(Collectors.toList());
        return answerOptions;
    }

    /**
     * 获取题目详细
     * @param questionId 题目ID
     * @return 题目和选项信息
     */
    @Override
    public QuestionVo getQuestionDetail(Long questionId) {
        return questionMapper.selectDetailById(questionId);
    }

    /**
     * 修改题目和答案
     * @param questionVo 题目和选项信息
     * @return 修改结果
     */
    @Override
    public int updateQuestionAndAnswer(QuestionVo questionVo) {
        Long questionId = questionVo.getQuestionId();
        // 修改题目
        Question question = new Question();
        BeanUtils.copyProperties(questionVo, question);
        questionMapper.updateById(question);
        // 删掉原有选项
        answerOptionMapper.delete(
                new LambdaUpdateWrapper<AnswerOption>()
                    .eq(AnswerOption::getQuestionId, questionId)
        );
        // 添加新选项
        List<AnswerOption> answerOptions = rmEmptyOptions(questionVo.getAnswerOptions());
        answerOptions.forEach(answerOption -> answerOption.setQuestionId(questionId));
        answerOptionMapper.insertBatch(answerOptions);
        return 1;
    }

    @Override
    public QuestionDetail getOneRandomQuestionDetail(Integer trainObject) {
        QuestionDetail questionDetail = new QuestionDetail();
        Question question = questionMapper.selectOneRandomQuestion(trainObject);
        BeanUtils.copyProperties(question, questionDetail);
        List<QuestionDetail.AnswerOption> simpleAnswerOptions = getAnswerOptionsByQuestion(question);
        questionDetail.setOptionList(simpleAnswerOptions);
        return questionDetail;
    }

    @Override
    public boolean checkAnswer(AnswerQuestion answerQuestion) {
        List<AnswerOption> correctAnswerList = getCorrectAnswerList(answerQuestion);
        List<Long> optionIdList = answerQuestion.getOptionIdList();
        List<Long> correctAnswerIdList = correctAnswerList.stream()
                .map(AnswerOption::getAnswerOptionId)
                .collect(Collectors.toList());
        return sameAnswer(optionIdList, correctAnswerIdList);
    }

    /**
     * 对题目回答进行评卷
     * @param answerQuestionList 题目回答list
     * @return map questionId, isTrue
     */
    @Override
    public Map<Long, Boolean> checkAnswer(List<AnswerQuestion> answerQuestionList) {
        // 获取所有试题的答案 TODO 加入Redis
        List<Long> questionIdList = answerQuestionList.stream()
                .map(AnswerQuestion::getQuestionId)
                .collect(Collectors.toList());
        Map<Long, List<Long>> correctAnswer = getCorrectAnswer(questionIdList);
        // 遍历对比，装入评卷Map
        Map<Long, Boolean> resMap = new HashMap<>();
        for (AnswerQuestion answerQuestion : answerQuestionList) {
            Long questionId = answerQuestion.getQuestionId();
            List<Long> optionIdList = answerQuestion.getOptionIdList();
            List<Long> correctAnswerIdList = correctAnswer.getOrDefault(questionId, new ArrayList<>());
            boolean isCorrect = sameAnswer(optionIdList, correctAnswerIdList);
            resMap.put(questionId, isCorrect);
        }
        return resMap;
    }

    /**
     * 对比 回答List 和 答案List 是否一致
     * @param answerIdList 回答
     * @param correctAnswerIdList 正确答案
     * @return 是否正确
     */
    public boolean sameAnswer(List<Long> answerIdList, List<Long> correctAnswerIdList) {
        if( answerIdList == null || correctAnswerIdList == null) {
            return false;
        }
        if( answerIdList.size() == correctAnswerIdList.size() ) {
            return answerIdList.containsAll(correctAnswerIdList);
        }
        return false;
    }

    /**
     * 通过问题 ID list 获取正确的答案
     * @param questionIds 问题 Id 列表
     * @return Map questionId : [answerId...]
     */
    @Override
    public Map<Long, List<Long>> getCorrectAnswer(List<Long> questionIds) {
        List<AnswerOption> correctAnswerOptions = answerOptionMapper.selectList(
                new LambdaQueryWrapper<AnswerOption>()
                        .in(AnswerOption::getQuestionId, questionIds)
                        .eq(AnswerOption::getIsCorrect, 1)
        );
        // 遍历答案存入 Map questionId : answerIds中
        HashMap<Long, List<Long>> correctAnswerMap = new HashMap<>();
        for (AnswerOption correctAnswerOption : correctAnswerOptions) {
            Long questionId = correctAnswerOption.getQuestionId();
            List<Long> correctAnswerIdList = correctAnswerMap.getOrDefault(questionId, new ArrayList<>());
            correctAnswerIdList.add(correctAnswerOption.getAnswerOptionId());
            correctAnswerMap.put(questionId, correctAnswerIdList);
        }
        return correctAnswerMap;
    }

    private List<AnswerOption> getCorrectAnswerList(AnswerQuestion answerQuestion) {
        List<AnswerOption> answerOptions = answerOptionMapper.selectList(
                new LambdaQueryWrapper<AnswerOption>()
                        .eq(AnswerOption::getQuestionId, answerQuestion.getQuestionId())
                        .eq(AnswerOption::getIsCorrect, 1)
        );
        return answerOptions;
    }

    private List<QuestionDetail.AnswerOption> getAnswerOptionsByQuestion(Question question) {
        Long questionId = question.getQuestionId();
        List<AnswerOption> answerOptions = answerOptionMapper.selectList(
                new LambdaQueryWrapper<AnswerOption>()
                        .eq(AnswerOption::getQuestionId, questionId)
                        .orderByAsc(AnswerOption::getSort)
        );
        return answerOptions.stream().map(answerOption -> {
            QuestionDetail.AnswerOption simpleAnswerOption = new QuestionDetail.AnswerOption();
            BeanUtils.copyProperties(answerOption, simpleAnswerOption);
            return simpleAnswerOption;
        }).collect(Collectors.toList());
    }
}
